home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 1.iso / DEMON / RISCOS2 / TCP_131S.ARC / c / FTPSERV < prev    next >
Text File  |  1994-03-03  |  20KB  |  775 lines

  1. /* FTP Server state machine - see RFC 959 */
  2.  
  3. #define LINELEN         128     /* Length of command buffer */
  4.  
  5. #include <stdarg.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <ctype.h>
  10. #include <time.h>
  11. #include "global.h"
  12. #include "mbuf.h"
  13. #include "netuser.h"
  14. #include "timer.h"
  15. #include "tcp.h"
  16. #include "ftp.h"
  17. #include "misc.h"
  18. #include "arc.h"
  19.  
  20. static void ftpscs(struct tcb *, char, char);
  21. static void ftpscr(struct tcb *, int16);
  22. static void ftpcommand(struct ftp *);
  23. static int  pport(struct socket *, char *);
  24. static void ftplogin(struct ftp *, char *);
  25.  
  26.  
  27. /* Command table */
  28. static char *commands[] = {
  29.   "user",
  30. #define USER_CMD        0
  31.   "acct",
  32. #define ACCT_CMD        1
  33.   "pass",
  34. #define PASS_CMD        2
  35.   "type",
  36. #define TYPE_CMD        3
  37.   "list",
  38. #define LIST_CMD        4
  39.   "cwd",
  40. #define CWD_CMD         5
  41.   "dele",
  42. #define DELE_CMD        6
  43.   "name",
  44. #define NAME_CMD        7
  45.   "quit",
  46. #define QUIT_CMD        8
  47.   "retr",
  48. #define RETR_CMD        9
  49.   "stor",
  50. #define STOR_CMD        10
  51.   "port",
  52. #define PORT_CMD        11
  53.   "nlst",
  54. #define NLST_CMD        12
  55.   "pwd",
  56. #define PWD_CMD         13
  57.   "xpwd",                 /* For compatibility with 4.2BSD */
  58. #define XPWD_CMD        14
  59.   "mkd ",
  60. #define MKD_CMD         15
  61.   "xmkd",                 /* For compatibility with 4.2BSD */
  62. #define XMKD_CMD        16
  63.   "xrmd",                 /* For compatibility with 4.2BSD */
  64. #define XRMD_CMD        17
  65.   "rmd ",
  66. #define RMD_CMD         18
  67.   "stru",
  68. #define STRU_CMD        19
  69.   "mode",
  70. #define MODE_CMD        20
  71.   NULLCHAR
  72. };
  73.  
  74. /* Response messages */
  75. static char banner[]   = "220 %s FTP server ready at %s\r\n";
  76. static char badcmd[]   = "500 Unknown command\r\n";
  77. static char unsupp[]   = "500 Unsupported command or option\r\n";
  78. static char givepass[] = "331 Enter PASS command\r\n";
  79. static char logged[]   = "230 Logged in\r\n";
  80. static char loggeda[]  = "230 Logged in as anonymous, restrictions apply\r\n";
  81. static char typeok[]   = "200 Type OK\r\n";
  82. static char only8[]    = "501 Only logical bytesize 8 supported\r\n";
  83. static char deleok[]   = "250 File deleted\r\n";
  84. static char mkdok[]    = "200 MKD ok\r\n";
  85. static char delefail[] = "550 Delete failed\r\n";
  86. static char pwdmsg[]   = "257 \"%s\" is current directory\r\n";
  87. static char badtype[]  = "501 Unknown type \"%s\"\r\n";
  88. static char badport[]  = "501 Bad port syntax\r\n";
  89. static char unimp[]    = "502 Command not yet implemented\r\n";
  90. static char bye[]      = "221 Goodbye!\r\n";
  91. static char nodir[]    = "553 Can't read directory \"%s\"\r\n";
  92. static char cantopen[] = "550 Can't read file \"%s\"\r\n";
  93. static char sending[]  = "150 Opening data connection for %s %s\r\n";
  94. static char cantmake[] = "553 Can't create \"%s\"\r\n";
  95. static char portok[]   = "200 Port command okay\r\n";
  96. static char rxok[]     = "226 File received OK\r\n";
  97. static char txok[]     = "226 File sent OK\r\n";
  98. static char noperm[]   = "550 Permission denied\r\n";
  99. static char noconn[]   = "425 Data connection reset\r\n";
  100. static char notlog[]   = "530 Please log in with USER and PASS\r\n";
  101. static char okay[]     = "200 Ok\r\n";
  102.  
  103. static struct tcb *ftp_tcb;
  104.  
  105. /* Start up FTP service */
  106. int ftp1(int argc, char **argv)
  107. {
  108.   struct socket lsocket;
  109.  
  110.   lsocket.address = ip_addr;
  111.   if(argc < 2)
  112.     lsocket.port = FTP_PORT;
  113.   else
  114.       lsocket.port = atoi(argv[1]);
  115.  
  116.   ftp_tcb = open_tcp(&lsocket, NULLSOCK, TCP_SERVER, 0, (void(*)())ftpscr, NULLVFP, (void(*)())ftpscs, 0, (char *) NULL);
  117.   return(0);
  118. }
  119. /* Shut down FTP server */
  120. int ftp0(void)
  121. {
  122.   if(ftp_tcb != NULLTCB)
  123.     close_tcp(ftp_tcb);
  124.   return(0);
  125. }
  126. /* FTP Server Control channel State change upcall handler */
  127. static void ftpscs(struct tcb *tcb, char old, char new)
  128. {
  129.   extern char hostname[];
  130.   struct ftp *ftp;
  131.   time_t t;
  132.   char *cp,*cp1;
  133.  
  134.   old = old;
  135.  
  136.   switch(new)
  137.   {
  138.     /* Setting QUICKSTART piggybacks the server's banner on the SYN/ACK segment;
  139.      * leaving it unset waits for the three-way handshake to complete before
  140.      * sending the banner. Piggybacking unfortunately breaks some old TCPs,
  141.      * so its use is not (yet) recommended.
  142.     */
  143. #ifdef  QUICKSTART
  144.   case SYN_RECEIVED:
  145. #else
  146.   case ESTABLISHED:
  147. #endif
  148.     if((ftp = ftp_create(LINELEN)) == NULLFTP)
  149.     {
  150.       /* No space, kill connection */
  151.       close_tcp(tcb);
  152.       return;
  153.     }
  154.     ftp->control = tcb;             /* Downward link */
  155.     tcb->user = (char *)ftp;        /* Upward link */
  156.  
  157.     /* Set default data port */
  158.     ftp->port.address = tcb->conn.remote.address;
  159.     ftp->port.port = FTPD_PORT;
  160.  
  161.     /* Note current directory */
  162.     log_event(tcb,"open FTP");
  163.     time(&t);
  164.     cp = ctime(&t);
  165.     if((cp1 = strchr(cp,'\n')) != NULLCHAR)
  166.       *cp1 = '\0';
  167.     tprintf(ftp->control,banner,hostname,cp);
  168.     break;          
  169.   case CLOSE_WAIT:
  170.     close_tcp(tcb);
  171.     break;
  172.   case CLOSED:
  173.     log_event(tcb,"close FTP");
  174.     if((ftp = (struct ftp *)tcb->user) != NULLFTP)
  175.       ftp_delete(ftp);
  176.     /* Check if server is being shut down */
  177.     if(tcb == ftp_tcb)
  178.       ftp_tcb = NULLTCB;
  179.     del_tcp(tcb);
  180.     break;
  181.   }
  182. }
  183.  
  184. /* FTP Server Control channel Receiver upcall handler */
  185. static void ftpscr(struct tcb *tcb, int16 cnt)
  186. {
  187.   register struct ftp *ftp;
  188.   char c;
  189.   struct mbuf *bp;
  190.  
  191.   cnt = cnt;
  192.  
  193.   if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  194.     /* Unknown connection, just kill it */
  195.     close_tcp(tcb);
  196.     return;
  197.   }
  198.   switch(ftp->state)
  199.   {
  200.   case COMMAND_STATE:
  201.     /* Assemble an input line in the session buffer. Return if incomplete */
  202.     recv_tcp(tcb,&bp,0);
  203.     while(pullone(&bp,&c) == 1)
  204.     {
  205.       switch(c)
  206.       {
  207.       case '\r':      /* Strip cr's */
  208.         continue;
  209.       case '\n':      /* Complete line; process it */
  210.         ftp->buf[ftp->cnt] = '\0';
  211.         ftpcommand(ftp);
  212.         ftp->cnt = 0;
  213.         break;
  214.       default:        /* Assemble line */
  215.         if(ftp->cnt != LINELEN-1)
  216.           ftp->buf[ftp->cnt++] = c;
  217.         break;
  218.       }
  219.     }
  220.     /* else no linefeed present yet to terminate command */
  221.     break;
  222.   case SENDING_FILE_STATE:
  223.   case SENDING_DATA_STATE:
  224.   case RECEIVING_STATE:
  225.     /* Leave commands pending on receive queue until
  226.                      * present command is done
  227.                      */
  228.     break;
  229.   }
  230. }
  231.  
  232. /* FTP server data channel connection state change upcall handler */
  233. void ftpsds(struct tcb *tcb, char old, char new)
  234. {
  235.   register struct ftp *ftp;
  236.  
  237.   if((ftp = (struct ftp *)tcb->user) == NULLFTP)
  238.   {
  239.     /* Unknown connection. Kill it */
  240.     del_tcp(tcb);
  241.   }
  242.   else if((old == FINWAIT1 || old == CLOSING) && (ftp->state == SENDING_FILE_STATE || ftp->state == SENDING_DATA_STATE)){
  243.     /* We've received an ack of our FIN while sending; we're done */
  244.     ftp->state = COMMAND_STATE;
  245.     tprintf(ftp->control,txok);
  246.     /* Kick command parser if something is waiting */
  247.     if(ftp->control->rcvcnt != 0)
  248.       ftpscr(ftp->control,ftp->control->rcvcnt);
  249.   } 
  250.   else if(ftp->state == RECEIVING_STATE && new == CLOSE_WAIT)
  251.   {
  252.     /* FIN received on incoming file */
  253.     close_tcp(tcb);
  254.     if(ftp->fp != stdout)
  255.       fclose(ftp->fp);
  256.     ftp->fp = NULLFILE;
  257.     ftp->state = COMMAND_STATE;
  258.     tprintf(ftp->control,rxok);
  259.     /* Kick command parser if something is waiting */
  260.     if(ftp->control->rcvcnt != 0)
  261.       ftpscr(ftp->control,ftp->control->rcvcnt);
  262.   }
  263.   else if(new == CLOSED)
  264.   {
  265.     if(tcb->reason != NORMAL)
  266.     {
  267.       /* Data connection was reset, complain about it */
  268.       tprintf(ftp->control,noconn);
  269.       /* And clean up */
  270.       if(ftp->fp != NULLFILE && ftp->fp != stdout)
  271.         fclose(ftp->fp);
  272.       ftp->fp = NULLFILE;
  273.       ftp->state = COMMAND_STATE;
  274.       /* Kick command parser if something is waiting */
  275.       if(ftp->control->rcvcnt != 0)
  276.         ftpscr(ftp->control,ftp->control->rcvcnt);
  277.     }
  278.     /* Clear only if another transfer hasn't already started */
  279.     if(ftp->data == tcb)
  280.       ftp->data = NULLTCB;
  281.     del_tcp(tcb);
  282.   }
  283. }
  284.  
  285. /* Parse and execute ftp commands */
  286. static void ftpcommand(register struct ftp *ftp)
  287. {
  288.   char *cmd,*arg,*cp,**cmdp,*file;
  289.   char *mode;
  290.   struct socket dport;
  291.   int i;
  292.  
  293.   cmd = ftp->buf;
  294.   if(ftp->cnt == 0)
  295.   {
  296.     /* Can't be a legal FTP command */
  297.     tprintf(ftp->control,badcmd);
  298.     return;
  299.   }       
  300.   cmd = ftp->buf;
  301.  
  302.   /* Translate entire buffer to lower case */
  303.   for(cp = cmd;*cp != '\0';cp++)
  304.     *cp = tolower(*cp);
  305.  
  306.   /* Find command in table; if not present, return syntax error */
  307.   for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  308.     if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  309.       break;
  310.   if(*cmdp == NULLCHAR)
  311.   {
  312.     tprintf(ftp->control,badcmd);
  313.     return;
  314.   }
  315.   /* Allow only USER, PASS and QUIT before logging in */
  316.   if(ftp->cd == NULLCHAR || ftp->path[0] == NULLCHAR)
  317.   {
  318.     switch(cmdp-commands)
  319.     {
  320.     case USER_CMD:
  321.     case PASS_CMD:
  322.     case QUIT_CMD:
  323.       break;
  324.     default:
  325.       tprintf(ftp->control,notlog);
  326.       return;
  327.     }
  328.   }
  329.   arg = &cmd[strlen(*cmdp)];
  330.   while(*arg == ' ')
  331.     arg++;
  332.  
  333.   /* Execute specific command */
  334.   switch(cmdp-commands)
  335.   {
  336.   case USER_CMD:
  337.     if((ftp->username = malloc((unsigned)strlen(arg)+1)) == NULLCHAR)
  338.     {
  339.       close_tcp(ftp->control);
  340.       break;
  341.     }
  342.     strcpy(ftp->username,arg);
  343.     tprintf(ftp->control,givepass);
  344.     /* erase all user info from possible previous session */
  345.     for(i = 0; i < MAXPATH; i++)
  346.     {
  347.       if(ftp->path[i] != NULLCHAR)
  348.       {
  349.         free(ftp->path[i]);
  350.         ftp->path[i] = NULLCHAR;
  351.       }
  352.       ftp->perms[i] = 0;
  353.     }
  354.     if(ftp->cd != NULLCHAR)
  355.     {
  356.       free(ftp->cd);
  357.       ftp->cd = NULLCHAR;
  358.     }
  359.     break;
  360.   case TYPE_CMD:
  361.     switch(arg[0])
  362.     {
  363.     case 'A':
  364.     case 'a':       /* Ascii */
  365.       ftp->type = ASCII_TYPE;
  366.       tprintf(ftp->control,typeok);
  367.       break;
  368.     case 'l':
  369.     case 'L':
  370.       while(*arg != ' ' && *arg != '\0')
  371.         arg++;
  372.       if(*arg == '\0' || *++arg != '8')
  373.       {
  374.         tprintf(ftp->control,only8);
  375.         break;
  376.       }       /* Note fall-thru */
  377.     case 'B':
  378.     case 'b':       /* Binary */
  379.     case 'I':
  380.     case 'i':       /* Image */
  381.       ftp->type = IMAGE_TYPE;
  382.       tprintf(ftp->control,typeok);
  383.       break;
  384.     default:        /* Invalid */
  385.       tprintf(ftp->control,badtype,arg);
  386.       break;
  387.     }
  388.     break;
  389.   case QUIT_CMD:
  390.     tprintf(ftp->control,bye);
  391.     close_tcp(ftp->control);
  392.     break;
  393.   case RETR_CMD:
  394.     /* Disk operation; return ACK now */
  395.     tcp_output(ftp->control);
  396.     file = pathname(ftp->cd,arg);
  397.     if(ftp->type == IMAGE_TYPE)
  398.       mode = binmode[READ_BINARY];
  399.     else
  400.       mode = "r";
  401.     if(!permcheck(ftp,RETR_CMD,file))
  402.     {
  403.       tprintf(ftp->control,noperm);
  404.     }
  405.     else if((ftp->fp = fopen(file,mode)) == NULLFILE){
  406.       tprintf(ftp->control,cantopen,file);
  407.     }
  408.     else
  409.     {
  410.       log_event(ftp->control,"RETR %s",file);
  411.       dport.address = ip_addr;
  412.       dport.port = FTPD_PORT;
  413.       ftp->state = SENDING_FILE_STATE;
  414.       tprintf(ftp->control,sending,"RETR",arg);
  415.       ftp->data = open_tcp(&dport, &ftp->port, TCP_ACTIVE,
  416.       0, NULLVFP, (void(*)())ftpdt, (void(*)())ftpsds, ftp->control->tos, (char *)ftp);
  417.     }
  418.     free(file);
  419.     break;
  420.   case STOR_CMD:
  421.     /* Disk operation; return ACK now */
  422.     tcp_output(ftp->control);
  423.     file = pathname(ftp->cd,arg);
  424.     if(ftp->type == IMAGE_TYPE)
  425.       mode = binmode[WRITE_BINARY];
  426.     else
  427.       mode = "w";
  428.     if(!permcheck(ftp,STOR_CMD,file))
  429.     {
  430.       tprintf(ftp->control,noperm);
  431.       free(file);
  432.       break;
  433.     }
  434.     else if((ftp->fp = fopen(file,mode)) == NULLFILE){
  435.       tprintf(ftp->control,cantmake,file);
  436.     }
  437.     else
  438.         {
  439.       log_event(ftp->control,"STOR %s",file);
  440.       dport.address = ip_addr;
  441.       dport.port = FTPD_PORT;
  442.       ftp->state = RECEIVING_STATE;
  443.       tprintf(ftp->control,sending,"STOR",arg);
  444.       ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  445.       0, (void(*)())ftpdr, NULLVFP, (void(*)())ftpsds, ftp->control->tos, (char *)ftp);
  446.     }
  447.     free(file);
  448.     break;
  449.   case PORT_CMD:
  450.     if(pport(&ftp->port,arg) == -1)
  451.     {
  452.       tprintf(ftp->control,badport);
  453.     }
  454.     else
  455.         {
  456.       tprintf(ftp->control,portok);
  457.     }
  458.     break;
  459.   case LIST_CMD:
  460.     /* Disk operation; return ACK now */
  461.     tcp_output(ftp->control);
  462.     file = pathname(ftp->cd,arg);
  463.     if(!permcheck(ftp,RETR_CMD,file))
  464.     {
  465.       tprintf(ftp->control,noperm);
  466.     }
  467.     else if((ftp->p = dir(file,1)) == NULLCHAR)
  468.     {
  469.       tprintf(ftp->control,nodir,file);
  470.     }
  471.     else
  472.         {
  473.       dport.address = ip_addr;
  474.       dport.port = FTPD_PORT;
  475.       ftp->state = SENDING_DATA_STATE;
  476.       ftp->cp    = ftp->p;
  477.       tprintf(ftp->control,sending,"LIST",file);
  478.       ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  479.       0,NULLVFP,(void(*)())ftpdt,(void(*)())ftpsds,ftp->control->tos,(char *)ftp);
  480.     }
  481.     free(file);
  482.     break;
  483.   case NLST_CMD:
  484.     /* Disk operation; return ACK now */
  485.     tcp_output(ftp->control);
  486.     file = pathname(ftp->cd,arg);
  487.     if(!permcheck(ftp,RETR_CMD,file))
  488.     {
  489.       tprintf(ftp->control,noperm);
  490.     }
  491.     else if((ftp->p = dir(file,0)) == NULLCHAR)
  492.     {
  493.       tprintf(ftp->control,nodir,file);
  494.     }
  495.     else
  496.         {
  497.       dport.address = ip_addr;
  498.       dport.port = FTPD_PORT;
  499.       ftp->state = SENDING_DATA_STATE;
  500.       ftp->cp    = ftp->p;
  501.       tprintf(ftp->control,sending,"NLST",file);
  502.       ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  503.       0,NULLVFP,(void(*)())ftpdt,(void(*)())ftpsds,ftp->control->tos,(char *)ftp);
  504.     }
  505.     free(file);
  506.     break;
  507.   case CWD_CMD:
  508.     tcp_output(ftp->control);       /* Disk operation; return ACK now */
  509.  
  510.     file = pathname(ftp->cd,arg);
  511.     if(!permcheck(ftp,RETR_CMD,file))
  512.     {
  513.       tprintf(ftp->control,noperm);
  514.       free(file);
  515.     }
  516.     else if(access(file,0) == 0)
  517.     { /* See if it exists */
  518.       /* Succeeded, record in control block */
  519.       free(ftp->cd);
  520.       ftp->cd = file;
  521.       tprintf(ftp->control,pwdmsg,file);
  522.     }
  523.     else
  524.         {
  525.       /* Failed, don't change anything */
  526.       tprintf(ftp->control,nodir,file);
  527.       free(file);
  528.     }
  529.     break;
  530.   case XPWD_CMD:
  531.   case PWD_CMD:
  532.     tprintf(ftp->control,pwdmsg,ftp->cd);
  533.     break;
  534.   case ACCT_CMD:          
  535.     tprintf(ftp->control,unimp);
  536.     break;
  537.   case DELE_CMD:
  538.     file = pathname(ftp->cd,arg);
  539.     if(!permcheck(ftp,DELE_CMD,file))
  540.     {
  541.       tprintf(ftp->control,noperm);
  542.     }
  543.     else if(remove(file) == 0)
  544.     {
  545.       tprintf(ftp->control,deleok);
  546.     }
  547.     else
  548.         {
  549.       tprintf(ftp->control,delefail);
  550.     }
  551.     free(file);
  552.     break;
  553.   case PASS_CMD:
  554.     tcp_output(ftp->control);       /* Send the ack now */
  555.     ftplogin(ftp,arg);                      
  556.     break;
  557.   case XMKD_CMD:
  558.   case MKD_CMD:
  559.     file = pathname(ftp->cd,arg);
  560.     if(!permcheck(ftp,MKD_CMD,file))
  561.     {
  562.       tprintf(ftp->control,noperm);
  563.     }
  564.     else if(mkdir(file) == 0)
  565.     {
  566.       tprintf(ftp->control,mkdok);
  567.     }
  568.     else
  569.         {
  570.       tprintf(ftp->control,cantmake);
  571.     }
  572.     free(file);
  573.     break;
  574.   case XRMD_CMD:
  575.   case RMD_CMD:
  576.     file = pathname(ftp->cd,arg);
  577.     if(!permcheck(ftp,RMD_CMD,file))
  578.     {
  579.       tprintf(ftp->control,noperm);
  580.     }
  581.     else if(rmdir(file) == 0)
  582.     {
  583.       tprintf(ftp->control,deleok);
  584.     }
  585.     else
  586.         {
  587.       tprintf(ftp->control,delefail);
  588.     }
  589.     free(file);
  590.     break;
  591.   case STRU_CMD:
  592.     if(tolower(arg[0]) != 'f')
  593.       tprintf(ftp->control,unsupp);
  594.     else
  595.         tprintf(ftp->control,okay);
  596.     break;
  597.   case MODE_CMD:
  598.     if(tolower(arg[0]) != 's')
  599.       tprintf(ftp->control,unsupp);
  600.     else
  601.         tprintf(ftp->control,okay);
  602.     break;
  603.   }
  604. }
  605. static int pport(struct socket *sock, char *arg)
  606. {
  607.   int32 n;
  608.   int i;
  609.  
  610.   n = 0;
  611.   for(i=0;i<4;i++)
  612.   {
  613.     n = atoi(arg) + (n << 8);
  614.     if((arg = strchr(arg,',')) == NULLCHAR)
  615.       return -1;
  616.     arg++;
  617.   }
  618.   sock->address = n;
  619.   n = atoi(arg);
  620.   if((arg = strchr(arg,',')) == NULLCHAR)
  621.     return -1;
  622.   arg++;
  623.   n = atoi(arg) + (n << 8);
  624.   sock->port = (int16)n;
  625.   return 0;
  626. }
  627.  
  628. /* Attempt to log in the user whose name is in ftp->username and password
  629.    in pass */
  630. static void ftplogin(struct ftp *ftp, char *pass)
  631. {
  632.   char buf[80];
  633.   FILE *fp;
  634.   char *user;
  635.   char *password;
  636.   char *root;
  637.   char *permissions;
  638.   int anony = 0;
  639.   int i;
  640.  
  641.   if ((fp = fopen(userfile, "r")) == NULLFILE)
  642.   {
  643.     /* Userfile doesn't exist */
  644.     tprintf(ftp->control, noperm);
  645.     cwprintf(NULL, "%s (file missing)", noperm);
  646.     return;
  647.   }
  648.   while (fgets(buf, sizeof(buf), fp) != NULLCHAR)
  649.   {
  650.     if (buf[0] == '#')
  651.       continue;       /* Comment */
  652.     if ((user = strtok(buf," \n\t")) == NULLCHAR)
  653.       continue;
  654.     if (stricmp(ftp->username, user) == 0)
  655.       break;          /* Found user name */
  656.   }
  657.   if (feof(fp))
  658.   {
  659.     /* User name not found in file or is incomplete */
  660.     fclose(fp);
  661.     tprintf(ftp->control,noperm);
  662.     cwprintf(NULL, "%s (user not found)", noperm);
  663.     return;
  664.   }
  665.   fclose(fp);
  666.  
  667.   if ((password = strtok(NULLCHAR," \n\t")) == NULLCHAR)
  668.   {
  669.     /* Password required, non given */
  670.     tprintf(ftp->control, noperm);
  671.     return;
  672.   }
  673.  
  674.   if (strcmp(password, "*") == 0)
  675.     anony = 1;      /* User ID is password-free */
  676.  
  677.   if (!anony && stricmp(password, pass) != 0)
  678.   {
  679.     /* Password required, but wrong one given */
  680.     tprintf(ftp->control, noperm);
  681.     return;
  682.   }
  683.  
  684.   root = strtok(NULLCHAR, " \n\t");
  685.  
  686.   for (i = 0; i < MAXPATH; i++)
  687.   {
  688.     if((permissions = strtok(NULLCHAR, " \n\t")) == NULLCHAR)
  689.     {
  690.       /* Permission field missing, assume end of line */
  691.       break;
  692.     }
  693.     ftp->path[i] = malloc((unsigned)strlen(root) + 1);
  694.     strcpy(ftp->path[i], root);
  695.     ftp->perms[i] = atoi(permissions);
  696.     if((root = strtok(NULLCHAR, " \n\t")) == NULLCHAR)
  697.     {
  698.       /* No next path field, so assume end of line */
  699.       break;
  700.     }
  701.   }
  702.  
  703.   /* Set up current directory and LAST specified path prefix */
  704.   for (i = MAXPATH - 1; i >= 0; i--)
  705.     if (ftp->perms[i])
  706.       break;
  707.  
  708.   ftp->cd = malloc((unsigned)strlen(ftp->path[i]) + 1);
  709.   strcpy(ftp->cd, ftp->path[i]);
  710.  
  711.   if (!anony)
  712.   {
  713.     tprintf(ftp->control, logged);
  714.     log_event(ftp->control, "%s logged in", ftp->username);
  715.   }
  716.   else
  717.   {
  718.     tprintf(ftp->control, loggeda);
  719.     log_event(ftp->control, "%s logged in, ID %s", ftp->username, pass);
  720.   }
  721. }               
  722.  
  723. /* Illegal characters in a RISC OS filename */
  724. char badchars[] = "\"[]|<>+=;,";
  725.  
  726. /* Return 1 if the file operation is allowed, 0 otherwise */
  727. int permcheck(struct ftp *ftp, int op, char *file)
  728. {
  729.   char *cp;
  730.   int i;
  731.  
  732.   if(file == NULLCHAR || ftp->path[0] == NULLCHAR)
  733.     return 0;       /* Probably hasn't logged in yet */
  734.   /* Check for characters illegal in RISC OS file names */
  735.   for(cp = badchars;*cp != '\0';cp++)
  736.   {
  737.     if(strchr(file,*cp) != NULLCHAR)
  738.       return 0;       
  739.   }
  740.   /* The target file must be under the users allowed path */
  741.   for(i = 0; i < MAXPATH; i++)
  742.     if(ftp->path[i] != NULLCHAR &&
  743.         strncmp(file, ftp->path[i], strlen(ftp->path[i])) == 0)
  744.       break;
  745.  
  746.   if (i == MAXPATH)
  747.     return 0;
  748.  
  749.   switch(op)
  750.   {
  751.   case RETR_CMD:
  752.     /* User must have permission to read files */
  753.     if(ftp->perms[i] & FTP_READ)
  754.       return 1;
  755.     return 0;
  756.   case DELE_CMD:
  757.   case RMD_CMD:
  758.     /* User must have permission to (over)write files */
  759.     if(ftp->perms[i] & FTP_WRITE)
  760.       return 1;
  761.     return 0;
  762.   case STOR_CMD:
  763.   case MKD_CMD:
  764.     /* User must have permission to (over)write files, or permission
  765.                      * to create them if the file doesn't already exist
  766.                      */
  767.     if(ftp->perms[i] & FTP_WRITE)
  768.       return 1;
  769.     if(access(file,2) == -1 && (ftp->perms[i] & FTP_CREATE))
  770.       return 1;
  771.     return 0;
  772.   }
  773.   return 0;       /* "can't happen" -- keep lint happy */
  774. }
  775.